﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Windows.Media.Imaging;
using CAION2.Commands;
using CAION2.Helpers;
using CAION2.Models;
using CAION2.ViewModels;

namespace CAION2.Classes
{
    static class ClassRecepture
    {
        private static Hashtable _itemsHash;

        private static byte _maxDeepLevel;
        private static byte _maxDeepLevelForMulti;
        private static readonly TreeNode DummyNode;

        static ClassRecepture()
        {
            DummyNode = new TreeNode("dummy", null, null) { IsRecipe = false, IsExpanded = false, IsApply = false, IsHidden = true};
        }

        public static TreeNode LoadTree(ICollection allRecipes, DataRow rootRecipe)
        {
            _maxDeepLevel = Convert.ToByte(ClassConfiguration.GetValue("MaxDeepLevel"));
            _maxDeepLevelForMulti = Convert.ToByte(ClassConfiguration.GetValue("MaxDeepLevelForMulti"));

            _itemsHash = new Hashtable(allRecipes.Count);

            foreach (DataRowView row in allRecipes)
            {
                CheckRowInItemsHash(row, "product");
                CheckRowInItemsHash(row, "combo1_product");
                CheckRowInItemsHash(row, "combo2_product");
                CheckRowInItemsHash(row, "combo3_product");
                CheckRowInItemsHash(row, "combo4_product");
            }

            TreeNode rootItemNode = new TreeNode(rootRecipe["product"].ToString().ToUpper(), null, null)
            {
                ProductQuantity = Convert.ToInt16(rootRecipe["product_quantity"])
            };
            CheckNodeForItemOrRecipe(ref rootItemNode, 1);

            return rootItemNode;
        }

        private static void CheckRowInItemsHash(DataRowView row, string fieldName)
        {
            string id = row[fieldName].ToString().ToUpper();
            if (string.IsNullOrEmpty(id)) return;

            if (!_itemsHash.ContainsKey(id))
            {
                List<Tuple<DataRowView, string>> rows = new List<Tuple<DataRowView, string>>
                {
                    new Tuple<DataRowView, string>(row, fieldName)
                };
                _itemsHash.Add(id, rows);
            }
            else
            {
                List<Tuple<DataRowView, string>> list = _itemsHash[id] as List<Tuple<DataRowView, string>>;
                if (list != null)
                {
                    list.Add(new Tuple<DataRowView, string>(row, fieldName));
                }
                else
                {
                    List<Tuple<DataRowView, string>> rows = new List<Tuple<DataRowView, string>>
                    {
                        new Tuple<DataRowView, string>(row, fieldName)
                    };
                    _itemsHash[id] = rows;
                }
            }
        }

        public static void CheckNodeForItemOrRecipe(ref TreeNode currentItemNode, byte deepLevel)
        {
            if (!_itemsHash.ContainsKey(currentItemNode.Id)) return;

            List<Tuple<DataRowView, string>> currentItemRecipes = _itemsHash[currentItemNode.Id] as List<Tuple<DataRowView, string>>;
            if (currentItemRecipes == null) return;

            if (currentItemRecipes.Count == 1)
            {
                GetComponentNodesFromOneRecipe(ref currentItemNode, currentItemRecipes[0].Item1, deepLevel);
            }
            else
            {
                GetComponentNodesFromManyRecipes(ref currentItemNode, currentItemRecipes, deepLevel);
            }
        }

        private static void GetComponentNodesFromManyRecipes(ref TreeNode currentItemNode, List<Tuple<DataRowView, string>> currentItemRecipes, byte deepLevel)
        {
            //TODO: При мульках бывают рецепты из других профессий! Надо добавить!

            currentItemNode.IsCriticalCraft = false;
            currentItemNode.RecipeLevel = 0;
            currentItemNode.RequireDp = 0;
            currentItemNode.IsRecipe = true;

            if (deepLevel > 0) deepLevel++;

            foreach (Tuple<DataRowView, string> recipe in currentItemRecipes)
            {
                TreeNode subItemNode = new TreeNode(recipe.Item1[recipe.Item2].ToString().ToUpper(), currentItemNode, "ItemsForRecipes")
                {
                    ComponentQuantity = Convert.ToInt16(Math.Ceiling((double)currentItemNode.ProductQuantity
                                                                        /Convert.ToInt16(recipe.Item1["product_quantity"])))
                };
                GetComponentNodesFromOneRecipe(ref subItemNode, recipe.Item1, deepLevel);
            }
        }

        private static void GetComponentNodesFromOneRecipe(ref TreeNode currentItemNode, DataRowView currentItemRecipe, byte deepLevel)
        {
            if (currentItemRecipe == null) return;
            if (deepLevel > _maxDeepLevel)
            {
                currentItemNode.ChildNodes.Add(DummyNode);
                currentItemNode.ChildNodes[0].ParentId = currentItemNode.Id;
                return;
            }

            currentItemNode.IsCriticalCraft = currentItemRecipe["product"].ToString() != currentItemNode.Id;
            currentItemNode.RecipeLevel = Convert.ToInt16(currentItemRecipe["required_skillpoint"]);
            currentItemNode.ProductQuantity = Convert.ToInt16(currentItemRecipe["product_quantity"]);

            if (currentItemRecipe["require_dp"] != DBNull.Value)
            {
                currentItemNode.RequireDp = Convert.ToInt16(currentItemRecipe["require_dp"]);
            }

            if (deepLevel > 0) deepLevel++;
            for (int i = 1; i <= 8; i++)
            {
                if (string.IsNullOrEmpty(currentItemRecipe["component" + i].ToString())) continue;

                TreeNode node = new TreeNode(currentItemRecipe["component" + i].ToString().ToUpper(), currentItemNode, "ItemsForRecipes")
                {
                    ComponentQuantity = Convert.ToInt16(currentItemRecipe[String.Format("compo{0}_quantity", i)].ToString())
                };
                CheckNodeForItemOrRecipe(ref node, deepLevel);
            }
        }
    }

    public class TreeNode : ViewModelBase
    {
        private readonly string _id;
        private TreeNode _parentNode;
        private Int16 _recipeLevel;
        private Int16 _requireDp;
        private bool _isApply;
        private bool _isExpanded;
        private bool _isHidden;
        private float _opacity;

        private int _totalDemand;


        public RelayCommand ApplyMaterialCommand { get; private set; }


        public TreeNode(string id, TreeNode parentNode, string tableName)
        {
            _id = id;
            _parentNode = parentNode;

            ChildNodes = new ObservableCollection<TreeNode>();

            if (parentNode != null)
            {
                parentNode.ChildNodes.Add(this);
            }

            if (string.IsNullOrEmpty(tableName)) return;

            Item = DataModel.GetRow(tableName, string.Format("name = '{0}'", id),
                string.Format("translate{0} as translate, icon_name, quality", DataModel.EngFix));

            string fileicon = DataModel.IconsDirectory + Item["icon_name"] + ".png";
            Icon = File.Exists(fileicon) ? new BitmapImage(new Uri(fileicon)) 
                : new BitmapImage(new Uri("/CAION2;component/UI2/IconSlot.png", UriKind.RelativeOrAbsolute));
            
            IsApply = true;
            IsExpanded = false;
            IsHidden = false;
            ProductQuantity = 1;

            ApplyMaterialCommand = new RelayCommand(ApplyMaterial) { IsEnabled = true };
        }

        public string Id
        {
            get { return _id; }
        }

        public string ParentId { get; set; }

        public TreeNode ParentNode
        {
            get { return _parentNode; }
            set
            {
                if (_parentNode == value) return;
                _parentNode = value;

                if (_parentNode != null && !_parentNode.ChildNodes.Contains(this))
                    _parentNode.ChildNodes.Add(this);
            }
        }

        public ObservableCollection<TreeNode> ChildNodes { get; set; }

        public DataRow Item { get; set; }

        public BitmapImage Icon { get; set; }

        public bool IsCriticalCraft { get; set; }

        public Int16 RecipeLevel
        {
            get { return _recipeLevel; }
            set
            {
                if (_recipeLevel == value) return;
                _recipeLevel = value;

                IsRecipe = _recipeLevel > 0;
            }
        }

        public bool IsRecipe { get; set; }

        public Int16 ProductQuantity { get; set; }

        public Int16 RequireDp
        {
            get { return _requireDp; }
            set
            {
                if (_requireDp == value) return;
                _requireDp = value;

                IsConvertRecipe = _requireDp > 0;
            }
        }

        public bool IsConvertRecipe { get; set; }

        public Int16 ComponentQuantity { get; set; }

        public bool IsApply
        {
            get { return _isApply; }
            set
            {
                if (_isApply == value) return;
                _isApply = value;

                OnPropertyChanged("IsApply");

                Opacity = _isApply ? 1f : 0.35f;
            }
        }

        public bool IsExpanded
        {
            get { return _isExpanded; }

            set
            {
                _isExpanded = value;
                OnPropertyChanged("IsExpanded");

                if (!HasDummyChild) return;
                ChildNodes.RemoveAt(0);
                LoadChildrenFromDummy();
            }
        }

        public bool IsHidden
        {
            get { return _isHidden; }

            set
            {
                _isHidden = value;
                OnPropertyChanged("IsHidden");
            }
        }

        public float Opacity
        {
            get { return _opacity; }
            set
            {
                if (Math.Abs(_opacity - value) < 0) return;
                _opacity = value;

                OnPropertyChanged("Opacity");
            }
        }

        public bool HasDummyChild
        {
            get
            {
                return ChildNodes.Count == 1 && ChildNodes[0].Id == "dummy";
            }
        }

        public int TotalDemand
        {
            get { return _totalDemand; }
            set
            {
                if (_totalDemand == value) return;
                _totalDemand = value;

                OnPropertyChanged("TotalDemand");
            }
        }
        


        private void ApplyMaterial()
        {
            IsApply = !IsApply;

            ApplyMaterial(this, IsApply);
            
            Mediator.Instance.NotifyColleagues(Messages.ReceptureCalculator, null);
        }

        private void ApplyMaterial(TreeNode parentNode, bool isApply)
        {
            foreach (TreeNode node in parentNode.ChildNodes)
            {
                node.IsApply = isApply;
                ApplyMaterial(node, isApply);
            }
        }
        
        private void LoadChildrenFromDummy()
        {
            UiServices.SetBusyState();

            TreeNode parentNode = this;
            ClassRecepture.CheckNodeForItemOrRecipe(ref parentNode, 1);
        }
    }

}
